#include "Stdafx.h"
#include "VertexInputLayout.h"

#include "../render/RenderDevice.h"
#include "ShaderObject.h"

namespace WindGE
{
	VertexInputLayout::VertexInputLayout() 
		: input_layout_(nullptr)
		, render_device_(nullptr)
	{ 
	}

	VertexInputLayout::~VertexInputLayout()
	{
		safe_release(input_layout_);
	}

	bool VertexInputLayout::create(RenderDevicePtr renderDevice, ID3D10Blob* shaderBytes, 
		unsigned int inputParamsNum, ID3D11ShaderReflection* shaderReflection)
	{
		render_device_ = renderDevice;

		std::vector<D3D11_INPUT_ELEMENT_DESC> inputElementList;
		unsigned int byteOffset = 0;
		unsigned int slot = -1;
		D3D11_INPUT_CLASSIFICATION inputClass = D3D11_INPUT_PER_VERTEX_DATA;
		for (unsigned int i = 0; i < inputParamsNum; ++i)
		{
			D3D11_SIGNATURE_PARAMETER_DESC inputParamDesc;
			shaderReflection->GetInputParameterDesc(i, &inputParamDesc);

			if (std::string(inputParamDesc.SemanticName) == "SV_InstanceID") continue;

			D3D11_INPUT_ELEMENT_DESC element;
			element.SemanticName = inputParamDesc.SemanticName;
			element.SemanticIndex = inputParamDesc.SemanticIndex;

			if (std::string(inputParamDesc.SemanticName) == "POSITION" ||
				std::string(inputParamDesc.SemanticName) == "TANGENT"  ||
				std::string(inputParamDesc.SemanticName) == "WEIGHTS"  ||
				std::string(inputParamDesc.SemanticName) == "BOUNDS")
			{
				slot++;
				byteOffset = 0;
				inputClass = D3D11_INPUT_PER_VERTEX_DATA;
			}
			else if (std::string(inputParamDesc.SemanticName) == "WORLDMATRIX" && 
					 inputParamDesc.SemanticIndex == 0)
			{
				slot++;
				byteOffset = 0;
				inputClass = D3D11_INPUT_PER_INSTANCE_DATA;
			}

			element.InputSlot = slot;
			element.AlignedByteOffset = byteOffset;
			element.InputSlotClass = inputClass;
			element.InstanceDataStepRate = 0;
			
			if (inputParamDesc.Mask == 1)
			{
				if (inputParamDesc.ComponentType == D3D_REGISTER_COMPONENT_UINT32)
					element.Format = DXGI_FORMAT_R32_UINT;
				else if (inputParamDesc.ComponentType == D3D_REGISTER_COMPONENT_SINT32)
					element.Format = DXGI_FORMAT_R32_SINT;
				else if (inputParamDesc.ComponentType == D3D_REGISTER_COMPONENT_FLOAT32)
					element.Format = DXGI_FORMAT_R32_FLOAT;
				byteOffset += 4;
			}
			else if (inputParamDesc.Mask <= 3)
			{
				if (inputParamDesc.ComponentType == D3D_REGISTER_COMPONENT_UINT32)
					element.Format = DXGI_FORMAT_R32G32_UINT;
				else if (inputParamDesc.ComponentType == D3D_REGISTER_COMPONENT_SINT32)
					element.Format = DXGI_FORMAT_R32G32_SINT;
				else if (inputParamDesc.ComponentType == D3D_REGISTER_COMPONENT_FLOAT32)
					element.Format = DXGI_FORMAT_R32G32_FLOAT;
				byteOffset += 8;
			}
			else if (inputParamDesc.Mask <= 7)
			{
				if (inputParamDesc.ComponentType == D3D_REGISTER_COMPONENT_UINT32)
					element.Format = DXGI_FORMAT_R32G32B32_UINT;
				else if (inputParamDesc.ComponentType == D3D_REGISTER_COMPONENT_SINT32)
					element.Format = DXGI_FORMAT_R32G32B32_SINT;
				else if (inputParamDesc.ComponentType == D3D_REGISTER_COMPONENT_FLOAT32)
					element.Format = DXGI_FORMAT_R32G32B32_FLOAT;
				byteOffset += 12;
			}
			else if (inputParamDesc.Mask <= 15)
			{
				if (inputParamDesc.ComponentType == D3D_REGISTER_COMPONENT_UINT32)
					element.Format = DXGI_FORMAT_R32G32B32A32_UINT;
			 	else if (inputParamDesc.ComponentType == D3D_REGISTER_COMPONENT_SINT32)
					element.Format = DXGI_FORMAT_R32G32B32A32_SINT;
				else if (inputParamDesc.ComponentType == D3D_REGISTER_COMPONENT_FLOAT32)
					element.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
				byteOffset += 16;
			}
			inputElementList.push_back(element);
		}

		safe_release(input_layout_);

		HRESULT hr = renderDevice->device()->CreateInputLayout(
			&inputElementList[0], inputElementList.size(), shaderBytes->GetBufferPointer(),
			shaderBytes->GetBufferSize(), &input_layout_);

		if (FAILED(hr))
		{
			//XdPrint("Create shader vertex input layout failed!\n");
			return false;
		}

		return true;
	}

	void VertexInputLayout::bind()
	{
		render_device_->device_context()->IASetInputLayout(input_layout_);
	}

}//end namespace WindGE
